/***************************************************************************/
/*                                                                         */
/*  gxvtrak.c                                                              */
/*                                                                         */
/*    TrueTypeGX/AAT trak table validation (body).                         */
/*                                                                         */
/*  Copyright 2004, 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/

/***************************************************************************/
/*                                                                         */
/* gxvalid is derived from both gxlayout module and otvalid module.        */
/* Development of gxlayout is supported by the Information-technology      */
/* Promotion Agency(IPA), Japan.                                           */
/*                                                                         */
/***************************************************************************/


#include "gxvalid.h"
#include "gxvcommn.h"


/*************************************************************************/
/*                                                                       */
/* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
/* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
/* messages during execution.                                            */
/*                                                                       */
#undef  FT_COMPONENT
#define FT_COMPONENT trace_gxvtrak


/*************************************************************************/
/*************************************************************************/
/*****                                                               *****/
/*****                      Data and Types                           *****/
/*****                                                               *****/
/*************************************************************************/
/*************************************************************************/

/*
 * referred track table format specification:
 * http://developer.apple.com/fonts/TTRefMan/RM06/Chap6trak.html
 * last update was 1996.
 * ----------------------------------------------
 * [MINIMUM HEADER]: GXV_TRAK_SIZE_MIN
 * version          (fixed:  32bit) = 0x00010000
 * format           (uint16: 16bit) = 0 is only defined (1996)
 * horizOffset      (uint16: 16bit)
 * vertOffset       (uint16: 16bit)
 * reserved         (uint16: 16bit) = 0
 * ----------------------------------------------
 * [VARIABLE BODY]:
 * horizData
 *   header         ( 2 + 2 + 4
 *   trackTable       + nTracks * ( 4 + 2 + 2 )
 *   sizeTable        + nSizes * 4 )
 * ----------------------------------------------
 * vertData
 *   header         ( 2 + 2 + 4
 *   trackTable       + nTracks * ( 4 + 2 + 2 )
 *   sizeTable        + nSizes * 4 )
 * ----------------------------------------------
 */
typedef struct  GXV_trak_DataRec_
{
    FT_UShort trackValueOffset_min;
    FT_UShort trackValueOffset_max;

} GXV_trak_DataRec, * GXV_trak_Data;


#define GXV_TRAK_DATA(FIELD)  GXV_TABLE_DATA(trak, FIELD)


/*************************************************************************/
/*************************************************************************/
/*****                                                               *****/
/*****                      UTILITY FUNCTIONS                        *****/
/*****                                                               *****/
/*************************************************************************/
/*************************************************************************/

static void
gxv_trak_trackTable_validate(FT_Bytes table,
                             FT_Bytes limit,
                             FT_UShort nTracks,
                             GXV_Validator valid)
{
    FT_Bytes p = table;

    FT_Fixed track;
    FT_UShort nameIndex;
    FT_UShort offset;
    FT_UShort i;


    GXV_NAME_ENTER("trackTable");

    GXV_TRAK_DATA(trackValueOffset_min) = 0xFFFFU;
    GXV_TRAK_DATA(trackValueOffset_max) = 0x0000;

    for (i = 0; i < nTracks; i++)
    {
        GXV_LIMIT_CHECK(4 + 2 + 2);
        track = FT_NEXT_LONG(p);
        nameIndex = FT_NEXT_USHORT(p);
        offset = FT_NEXT_USHORT(p);

        if (offset < GXV_TRAK_DATA(trackValueOffset_min))
            GXV_TRAK_DATA(trackValueOffset_min) = offset;
        if (offset > GXV_TRAK_DATA(trackValueOffset_max))
            GXV_TRAK_DATA(trackValueOffset_max) = offset;

        gxv_sfntName_validate(nameIndex, 256, 32767, valid);
    }

    valid->subtable_length = p - table;
    GXV_EXIT;
}


static void
gxv_trak_trackData_validate(FT_Bytes table,
                            FT_Bytes limit,
                            GXV_Validator valid)
{
    FT_Bytes p = table;
    FT_UShort nTracks;
    FT_UShort nSizes;
    FT_ULong sizeTableOffset;

    GXV_ODTECT(4, odtect);


    GXV_ODTECT_INIT(odtect);
    GXV_NAME_ENTER("trackData");

    /* read the header of trackData */
    GXV_LIMIT_CHECK(2 + 2 + 4);
    nTracks = FT_NEXT_USHORT(p);
    nSizes = FT_NEXT_USHORT(p);
    sizeTableOffset = FT_NEXT_ULONG(p);

    gxv_odtect_add_range(table, p - table, "trackData header", odtect);

    /* validate trackTable */
    gxv_trak_trackTable_validate(p, limit, nTracks, valid);
    gxv_odtect_add_range(p, valid->subtable_length,
                         "trackTable", odtect);

    /* sizeTable is array of FT_Fixed, don't check contents */
    p = valid->root->base + sizeTableOffset;
    GXV_LIMIT_CHECK(nSizes * 4);
    gxv_odtect_add_range(p, nSizes * 4, "sizeTable", odtect);

    /* validate trackValueOffet */
    p = valid->root->base + GXV_TRAK_DATA(trackValueOffset_min);
    if (limit - p < nTracks * nSizes * 2)
        GXV_TRACE(("too short trackValue array\n"));

    p = valid->root->base + GXV_TRAK_DATA(trackValueOffset_max);
    GXV_LIMIT_CHECK(nSizes * 2);

    gxv_odtect_add_range(valid->root->base
                         + GXV_TRAK_DATA(trackValueOffset_min),
                         GXV_TRAK_DATA(trackValueOffset_max)
                         - GXV_TRAK_DATA(trackValueOffset_min)
                         + nSizes * 2,
                         "trackValue array", odtect);

    gxv_odtect_validate(odtect, valid);

    GXV_EXIT;
}


/*************************************************************************/
/*************************************************************************/
/*****                                                               *****/
/*****                          trak TABLE                           *****/
/*****                                                               *****/
/*************************************************************************/
/*************************************************************************/

FT_LOCAL_DEF(void)
gxv_trak_validate(FT_Bytes table,
                  FT_Face face,
                  FT_Validator ftvalid)
{
    FT_Bytes p = table;
    FT_Bytes limit = 0;
    FT_Offset table_size;

    GXV_ValidatorRec validrec;
    GXV_Validator valid = &validrec;
    GXV_trak_DataRec trakrec;
    GXV_trak_Data trak = &trakrec;

    FT_ULong version;
    FT_UShort format;
    FT_UShort horizOffset;
    FT_UShort vertOffset;
    FT_UShort reserved;


    GXV_ODTECT(3, odtect);

    GXV_ODTECT_INIT(odtect);
    valid->root = ftvalid;
    valid->table_data = trak;
    valid->face = face;

    limit = valid->root->limit;
    table_size = limit - table;

    FT_TRACE3(("validating `trak' table\n"));
    GXV_INIT;

    GXV_LIMIT_CHECK(4 + 2 + 2 + 2 + 2);
    version = FT_NEXT_ULONG(p);
    format = FT_NEXT_USHORT(p);
    horizOffset = FT_NEXT_USHORT(p);
    vertOffset = FT_NEXT_USHORT(p);
    reserved = FT_NEXT_USHORT(p);

    GXV_TRACE((" (version = 0x%08x)\n", version));
    GXV_TRACE((" (format = 0x%04x)\n", format));
    GXV_TRACE((" (horizOffset = 0x%04x)\n", horizOffset));
    GXV_TRACE((" (vertOffset = 0x%04x)\n", vertOffset));
    GXV_TRACE((" (reserved = 0x%04x)\n", reserved));

    /* Version 1.0 (always:1996) */
    if (version != 0x00010000UL)
        FT_INVALID_FORMAT;

    /* format 0 (always:1996) */
    if (format != 0x0000)
        FT_INVALID_FORMAT;

    GXV_32BIT_ALIGNMENT_VALIDATE(horizOffset);
    GXV_32BIT_ALIGNMENT_VALIDATE(vertOffset);

    /* Reserved Fixed Value (always) */
    if (reserved != 0x0000)
        FT_INVALID_DATA;

    /* validate trackData */
    if (0 < horizOffset)
    {
        gxv_trak_trackData_validate(table + horizOffset, limit, valid);
        gxv_odtect_add_range(table + horizOffset, valid->subtable_length,
                             "horizJustData", odtect);
    }

    if (0 < vertOffset)
    {
        gxv_trak_trackData_validate(table + vertOffset, limit, valid);
        gxv_odtect_add_range(table + vertOffset, valid->subtable_length,
                             "vertJustData", odtect);
    }

    gxv_odtect_validate(odtect, valid);

    FT_TRACE4(("\n"));
}


/* END */